﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Xnaml.Demo {
	public enum TimerFormat {
		DownMilliseconds = -3,
		DownSeconds,
		DownMinutes,
		UpMilliseconds,
		UpSeconds,
		UpMinutes,
	}

	public class Timer {
		public Action<Timer> Tick;
		protected bool valid, updatable;
		protected TimerFormat format;
		protected double target, count, lag, last;
		protected int alltimeticks, repeat, ticks;

		public double Target {
			get {
				return target;
			}
		}

		public bool Active {
			get {
				return valid;
			}
		}

		public bool Inactive {
			get {
				return !valid;
			}
		}

		public int Ticks {
			get {
				return ticks;
			}
		}

		public double PreciseIncomplete {
			get {
				return 1.0 - ( count / target );
			}
		}

		public double PreciseComplete {
			get {
				return count / target;
			}
		}

		public float Incomplete {
			get {
				return (float)( 1.0 - ( count / target ) );
			}
		}

		public float Complete {
			get {
				return (float)( count / target );
			}
		}

		public TimerFormat Format {
			get {
				return format;
			}
			set {
				format = value;
			}
		}

		public int TotalTicks {
			get {
				return alltimeticks;
			}
		}

		public int Repeat {
			get {
				return repeat;
			}
			set {
				repeat = value;
				if ( repeat < alltimeticks ) {
					Stop( );
				}
			}
		}

		private void Defaults ( ) {
			Tick = null;
			format = TimerFormat.DownMilliseconds;
			valid = updatable = false;
			repeat = -1;
			count = 0;
			target = 1000;
		}

		public Timer ( ) {
			Defaults( );
		}

		public void Reset ( ) {
			count = 0;
			alltimeticks++;
			ticks++;
			if ( repeat < 0 || alltimeticks >= repeat )
				Stop( );
		}

		public void Force ( ) {
			if ( Tick != null )
				Tick( this );
			Reset( );
		}

		public void HardReset ( ) {
			alltimeticks = 0;
			count = 0;
			ticks = 0;
			valid = false;
		}

		public void Start ( double ms ) {
			valid = updatable = true;
			last = target = ms;
			count = 0;
			ticks = 0;
		}

		public void Start ( double ms, int repeats ) {
			valid = updatable = true;
			last = target = ms;
			repeat = repeats;
			count = 0;
			ticks = 0;
		}

		public void Restart ( ) {
			target = last;
			count = 0;
			valid = updatable = true;
		}

		public void Pause ( ) {
			updatable = false;
		}

		public void Stop ( ) {
			valid = false;
			updatable = false;
		}

		public void Update ( GameTime gametime ) {
			if ( updatable ) {
				count += gametime.ElapsedGameTime.TotalMilliseconds;
				if ( count >= target ) {
					lag = count - target;
					count = target;
					if ( Tick != null )
						Tick( this );
					Reset( );
				}
			}
		}

		public void ToString ( StringBuilder onto, TimerFormat format ) {
			double countup = target - count;
			switch ( format ) {
				case TimerFormat.DownMilliseconds:
					onto.AppendFormat( "#0", countup );
					break;
				case TimerFormat.UpMilliseconds:
					onto.AppendFormat( "#0", count );
					break;
				case TimerFormat.UpMinutes:
					double secsu = 60 * ( ( countup / 60000 ) - Math.Floor( countup / 60000 ) );
					onto.AppendFormat( "#0", countup );
					onto.AppendFormat( "#0'.'", countup / 60000 );
					onto.AppendFormat( "0#", secsu );
					break;
				case TimerFormat.DownMinutes:
					double secsd = 60 * ( ( count / 60000 ) - Math.Floor( countup / 60000 ) );
					onto.AppendFormat( "#0", count );
					onto.AppendFormat( "#0'.'", count / 60000 );
					onto.AppendFormat( "0#", secsd );
					break;
				case TimerFormat.UpSeconds:
					onto.AppendFormat( "#0.00", countup );
					break;
				default:
					onto.AppendFormat( "#0.00", count );
					break;
			}
		}

		public override string ToString ( ) {
			double countup = target - count;
			switch ( format ) {
				case TimerFormat.DownMilliseconds:
					return countup.ToString( "#0" );
				case TimerFormat.UpMilliseconds:
					return count.ToString( "#0" );
				case TimerFormat.UpMinutes:
					double secsd = 60 * ( ( countup / 60000 ) - Math.Floor( countup / 60000 ) );
					return ( count / 60000 ).ToString( "#0'.'" ) + secsd.ToString( "0#" );
				case TimerFormat.DownMinutes:
					double secsu = 60 * ( ( count / 60000 ) - Math.Floor( count / 60000 ) );
					return ( count / 60000 ).ToString( "#0'.'" ) + secsu.ToString( "0#" );
				case TimerFormat.UpSeconds:
					return ( countup / 1000 ).ToString( "#0.00" );
				default:
					return ( count / 1000 ).ToString( "#0.00" );
			}
		}
	}
}
